/**
 * @file PMT.c
 *
 * @author Luc Fourestier
 * @date 2009
 */


#include "PMT.h"

#include "Global.h"


#define CRC_SIZE 4
#define MIN_PMT_SIZE 17+CRC_SIZE


/**
 * Create the PMT_t structure
 *
 * @param Buffer: The incoming buffer containing the PMT
 * @param BuffSize: IN/OUT The buffer max size and the actual size parsed as output
 * @param PMT: The PMT output pointer (must be allocated by caller
 * @return errors
 */
int PMT_Create(unsigned char * Buffer, unsigned int *Size, PMT_t * PMT)
{
#define step(n) Index += n

	int RetVal = OK;
	unsigned int Index = 0;

	if((PMT != NULL) && (Buffer != NULL) && (Size != NULL) && (*Size >= MIN_PMT_SIZE))
	{
		unsigned int Temp;

		PMT->table_id = GET_U8(Buffer, Index);
		step(1);
		PMT->section_syntax_indicator = (GET_U8(Buffer, Index) & 0x80) >> 7;
		PMT->zero = (GET_U8(Buffer, Index) & 0x40) >> 6;
		PMT->reserved_1 = (GET_U8(Buffer, Index) & 0x30) >> 4;
		PMT->section_length = GET_U16(Buffer, Index) & 0xfff;
		step(2);
		PMT->transport_stream_id = GET_U16(Buffer, Index);
		step(2);
		PMT->reserved_2 = (GET_U8(Buffer, Index) & 0xc0) >> 6;
		PMT->version_number = (GET_U8(Buffer, Index) & 0x3e) >> 1;
		PMT->current_next_indicator = GET_U8(Buffer, Index) & 0x1;
		step(1);
		PMT->section_number = GET_U8(Buffer, Index);
		step(1);
		PMT->last_section_number = GET_U8(Buffer, Index);
		step(1);
		PMT->reserved_3 = (GET_U8(Buffer, Index) & 0xe0) >> 5;
		PMT->PCR_PID = GET_U16(Buffer, Index) & 0x1fff;
		step(2);
		PMT->reserved_4 = (GET_U8(Buffer, Index) & 0xf0) >> 4;
		PMT->program_info_length = GET_U16(Buffer, Index) & 0xfff;
		step(2);

		Temp = Index + PMT->program_info_length;
		while((Index < (*Size-CRC_SIZE)) && (Index < Temp))
		{
			step(1);
		}

		PMT->StreamDescNumber = 0;

		Temp = 3 + PMT->section_length - CRC_SIZE;
		while((Index < (*Size-CRC_SIZE)) && (Index < Temp))
		{
			if (PMT->StreamDescNumber < MAX_STREAM_DESC) // Because of table limitation
			{
				unsigned int Temp1;

				PMT->stream_descriptors[PMT->StreamDescNumber].stream_type = GET_U8(Buffer, Index);
				step(1);
				PMT->stream_descriptors[PMT->StreamDescNumber].reserved_1 = (GET_U8(Buffer, Index) & 0xe0) >> 5;
				PMT->stream_descriptors[PMT->StreamDescNumber].elementary_PID = GET_U16(Buffer, Index) & 0x1fff;
				step(2);
				PMT->stream_descriptors[PMT->StreamDescNumber].reserved_2 = (GET_U8(Buffer, Index) & 0xf0) >> 4;
				PMT->stream_descriptors[PMT->StreamDescNumber].ES_info_length = GET_U16(Buffer, Index) & 0xfff;
				step(2);

				Temp1 = Index + PMT->stream_descriptors[PMT->StreamDescNumber].ES_info_length;
				while((Index < (*Size-CRC_SIZE)) && (Index < Temp1))
				{
					step(1);
				}

				PMT->StreamDescNumber++;
			}
			else
			{
				// Discard the rest
				step(1);
			}
		}


		PMT->CRC_32 = GET_U32(Buffer, Index);
		step(4);

		*Size = Index;
	}
	else
	{
		RetVal = ERROR;
	}


	return(RetVal);

#undef step
}

/**
 * Print the PMT in details.
 *
 * @param PMT (in(: The PMT to print
 * @param Name (in): The name of the PMT (any nickname you wish! Can be NULL).
 * @param level (in): Level of details (Lowest = 0).
 * @param Output (in): Where to print (stdout or FILE).
 * @return Errors
 */
int PMT_Print(PMT_t * PMT, char * Name, int level, FILE * Output)
{
	int RetVal = OK;
	int i = 0;
	int Min = 0;

	if ((PMT == NULL) && (Output == NULL))
	{
		return(ERROR);
	}

	if (Name == NULL)
	{
		Name = "PMT";
	}

	fprintf(Output, "%1$s table_id: %2$i (0x%2$x)\n", Name, (int)PMT->table_id);
	fprintf(Output, "%1$s section_syntax_indicator: %2$i (0x%2$x)\n", Name, (int)PMT->section_syntax_indicator);
	fprintf(Output, "%1$s section_length: %2$i (0x%2$x)\n", Name, (int)PMT->section_length);
	fprintf(Output, "%1$s transport_stream_id: %2$i (0x%2$x)\n", Name, (int)PMT->transport_stream_id);
	fprintf(Output, "%1$s version_number: %2$i (0x%2$x)\n", Name, (int)PMT->version_number);
	fprintf(Output, "%1$s current_next_indicator: %2$i (0x%2$x)\n", Name, (int)PMT->current_next_indicator);
	fprintf(Output, "%1$s section_number: %2$i (0x%2$x)\n", Name, (int)PMT->section_number);
	fprintf(Output, "%1$s last_section_number: %2$i (0x%2$x)\n", Name, (int)PMT->last_section_number);
	fprintf(Output, "%1$s PCR_PID: %2$i (0x%2$x)\n", Name, (int)PMT->PCR_PID);
	fprintf(Output, "%1$s program_info_length: %2$i (0x%2$x)\n", Name, (int)PMT->program_info_length);
	fprintf(Output, "%1$s StreamDescNumber: %2$i (0x%2$x)\n", Name, (int)PMT->StreamDescNumber);

	Min = PMT->StreamDescNumber > MAX_STREAM_DESC ? MAX_STREAM_DESC : PMT->StreamDescNumber;
	for (i=0; i<Min; i++)
	{
		fprintf(Output, "%s stream_descriptors[%i]\n", Name, i);
		fprintf(Output, "\t stream_type: %1$i (0x%1$x)\n", (int)PMT->stream_descriptors[i].stream_type);
		fprintf(Output, "\t elementary_PID: %1$i (0x%1$x)\n", (int)PMT->stream_descriptors[i].elementary_PID);
	}

	fprintf(Output, "\n");

	return(RetVal);
}

/**
 * Check if the buffer is really a PMT
 *
 * @param Buffer (in): The input buffer
 * @param Size (in): The buffer size
 * @return OK if contains PMT, else PMT_ERROR_NOT_A_PMT
 */
int PMT_CheckPMT(unsigned char * Buffer, unsigned int Size)
{
	int RetVal = OK;
	unsigned char table_id = 0xff;

	if((Buffer != NULL) && (Size >= MIN_PMT_SIZE))
	{
		table_id = GET_U8(Buffer, 0);
		if (table_id != PMT_TID)
		{
			RetVal = PMT_ERROR_NOT_A_PMT;
		}
	}
	else
	{
		RetVal = ERROR;
	}

	return(RetVal);
}


